自動測試時除了檢查加入新資料,有時我們也會希望檢查舊資料是否成功地被移除。
今天我們用一個新的功能,來展示如何針對不應該存在的資料進行檢查。
我們多加一個新的函數,可以接收 listOf(tag)
,來更新 user
所有的 tag
重點是,我們這邊更新的內容,不僅僅是針對單一個 user
我們還接受傳 listOf(user)
,一次更新所有的 user.tags
fun updateUsersTags(users: List<User>, tags: List<Tag>) {
}
像昨天一樣,我們在 tests/kotlin/
資料夾內,建立 UpdateUsersTagsKtTest.kt
檔案,並加上幾個測試案例
internal class UpdateUsersTagsKtTest {
fun `測試單一全新用戶加上標籤`() {
Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;", driver = "org.h2.Driver")
transaction {
SchemaUtils.create(Users)
SchemaUtils.create(Tags)
SchemaUtils.create(UsersTags)
val testUser = User.new {
name = "TestUser"
}
val testTag = Tag.new {
name = "TestTag"
}
updateUsersTags(listOf(testUser), listOf(testTag))
assertEquals(
listOf(testTag),
testUser.tags.toList()
)
}
}
}
運作測試看看,我們發現測試並沒有通過
expected:<[Tag@2380d06e]> but was:<[]>
Expected :[Tag@2380d06e]
Actual :[]
這是理所當然的,因為我們的 updateUsersTags()
裡面還是空的
我們根據昨天的 userAddTag()
稍微調整一下寫法,看看能不能通過
fun updateUsersTags(users: List<User>, tags: List<Tag>) {
transaction {
users.forEach {
it.tags = SizedCollection(it.tags.toList() + tags)
}
}
}
重新運作測試,就可以通過了。
我們多加另一個測試的案例 測試多個用戶加上標籤
@Test
fun `測試多個用戶加上標籤`() {
Database.connect(
"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;",
driver = "org.h2.Driver"
)
transaction {
SchemaUtils.create(Users)
SchemaUtils.create(Tags)
SchemaUtils.create(UsersTags)
val testUser = User.new {
name = "TestUser"
}
val testUser2 = User.new {
name = "TestUse2"
}
val testTag = Tag.new {
name = "TestTag"
}
updateUsersTags(listOf(testUser, testUser2), listOf(testTag))
assertEquals(listOf(testTag), testUser.tags.toList())
assertEquals(listOf(testTag), testUser2.tags.toList())}
}
這個測試也可以成功通過
為求測試的完整,我們再加上 測試已有標籤用戶更動新標籤
@Test
fun `測試已有標籤用戶更動新標籤`() {
Database.connect(
"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;",
driver = "org.h2.Driver"
)
transaction {
SchemaUtils.create(Users)
SchemaUtils.create(Tags)
SchemaUtils.create(UsersTags)
val testUser = User.new {
name = "TestUser"
}
val testTag = Tag.new {
name = "TestTag"
}
val testTag2 = Tag.new {
name = "TestTag2"
}
testUser.tags = SizedCollection(listOf(testTag))
updateUsersTags(listOf(testUser), listOf(testTag2))
assertEquals(listOf(testTag2), testUser.tags.toList())
}
}
執行之後,我們發現以下錯誤
expected:<[Tag@641aca5a]> but was:<[Tag@60c77761, Tag@641aca5a]>
Expected :[Tag@641aca5a]
Actual :[Tag@60c77761, Tag@641aca5a]
這是怎麼回事呢?不是只是拿昨天的程式做了一點調整嗎?
和我們的需求邏輯比較之後,我們會發現,這是因為昨天的邏輯是「新增」,但是今天需求的邏輯是「更新」導致的。
我們需要再調整一下我們的程式
fun updateUsersTags(users: List<User>, tags: List<Tag>) {
transaction {
users.forEach {
it.tags = SizedCollection(tags)
}
}
}
這樣的話,我們的 測試已有標籤用戶更動新標籤
就會通過了。
我們可以再增加一些測試案例,像是
測試多個已有標籤用戶更動新標籤
測試多個已有標籤用戶更動多個新標籤
@Test
fun `測試多個已有標籤用戶更動新標籤`() {
Database.connect(
"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;",
driver = "org.h2.Driver"
)
transaction {
SchemaUtils.create(Users)
SchemaUtils.create(Tags)
SchemaUtils.create(UsersTags)
val testUser = User.new {
name = "TestUser"
}
val testUser2 = User.new {
name = "TestUser2"
}
val testTag = Tag.new {
name = "TestTag"
}
val testTag2 = Tag.new {
name = "TestTag2"
}
testUser.tags = SizedCollection(listOf(testTag))
testUser2.tags = SizedCollection(listOf(testTag))
updateUsersTags(listOf(testUser, testUser2), listOf(testTag2))
assertEquals(listOf(testTag2), testUser.tags.toList())
assertEquals(listOf(testTag2), testUser2.tags.toList())
}
}
@Test
fun `測試多個已有標籤用戶更動多個新標籤`() {
Database.connect(
"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;",
driver = "org.h2.Driver"
)
transaction {
SchemaUtils.create(Users)
SchemaUtils.create(Tags)
SchemaUtils.create(UsersTags)
val testUser = User.new {
name = "TestUser"
}
val testUser2 = User.new {
name = "TestUser2"
}
val oldTag = Tag.new {
name = "oldTag"
}
val testTag = Tag.new {
name = "TestTag"
}
val testTag2 = Tag.new {
name = "TestTag2"
}
testUser.tags = SizedCollection(listOf(oldTag))
testUser2.tags = SizedCollection(listOf(oldTag))
updateUsersTags(listOf(testUser, testUser2), listOf(testTag, testTag2))
assertEquals(listOf(testTag, testTag2), testUser.tags.toList())
assertEquals(listOf(testTag, testTag2), testUser2.tags.toList())
}
}
這些測試都可以順利通過,代表我們的程式應該是沒有問題的。
不過,雖然程式本身沒有問題,但是剛剛「更新」和「新增」之間的邏輯區分,確實讓我們花了點時間確認,未來的工程師可能在這裡也會遇到類似的邏輯問題。
如果我們想要強調這段程式是「更新」而不是「新增」,自動測試也能幫助我們嗎?
上面的問題,答案是:可以的!
我們可以加上一個案例 測試更新已有標籤用戶時應移除舊標籤
。這樣一來,如果有人不小心將這段程式,改成沒有移除舊標籤的版本,他就會從錯誤訊息內,看到這個未通過的測試案例,進而提醒他應該要移除掉舊標籤。
測試更新已有標籤用戶時應移除舊標籤
這個測試案例,要強調「應移除舊標籤」,所以用之前的 assertEquals()
斷言是不夠的。
這邊我們要利用 org.junit.Assert.*
套件的 assertThat()
,搭配上 org.hamcrest.CoreMatchers.*
套件的 not()
和 hasItem()
,來協助我們斷言舊標籤不存在於更新之後的 user.tags
內
我們來實作一下 測試更新已有標籤用戶時應移除舊標籤
fun `測試更新已有標籤用戶時應移除舊標籤`() {
Database.connect(
"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;",
driver = "org.h2.Driver"
)
transaction {
SchemaUtils.create(Users)
SchemaUtils.create(Tags)
SchemaUtils.create(UsersTags)
val testUser = User.new {
name = "TestUser"
}
val oldTag = Tag.new {
name = "oldTag"
}
val newTag = Tag.new {
name = "newTag"
}
testUser.tags = SizedCollection(listOf(oldTag))
updateUsersTags(listOf(testUser), listOf(newTag))
assertThat(testUser.tags.toList(), not(hasItem(oldTag)))
}
這個測試案例運行通過之後,可以保證之後的修改,如果不小心在執行 updateUsersTags()
時,沒有移除舊標籤的話,可以在自動測試的階段就被發現到。